﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Quexcel.OpenXML.Interfaces;
using System.IO.Packaging;
using Quexcel.OpenXML.Transaction;
using System.Text.RegularExpressions;
using Quexcel.OpenXML.Excel.Factory;
using Quexcel.OpenXML.Exceptions;
using Quexcel.OpenXML.Excel.Workbook;
using System.Xml;
using System.Collections;
using Quexcel.OpenXML.Excel.SharedString;
using Quexcel.OpenXML.Excel.Worksheet.Components;

namespace Quexcel.OpenXML.Excel.Worksheet
{
    internal class Worksheet : WorksheetComponent, IWorksheet
    {
        private string worksheetName;
        private IWorksheetReader worksheetReader;
        private static IWorksheetWriter worksheetWriter;
        private ExcelTransaction transaction;
        protected Quexcel.OpenXML.Excel.Workbook.Workbook workbook;

        protected int startRowIndex = 1;
        protected int endRowIndex = -1;
        protected int startCellIndex = 1;
        protected int endCellIndex = -1;

        public Worksheet(Quexcel.OpenXML.Excel.Workbook.Workbook workbook, ExcelTransaction transaction, PackagePart sheetPart, string sheetName)
            : base(sheetPart)
        {
            this.workbook = workbook;
            worksheetName = sheetName;
            this.transaction = transaction;

            Initialize();
        }

        protected Worksheet(Quexcel.OpenXML.Excel.Workbook.Workbook workbook, Worksheet worksheet)
            : base(worksheet.SheetPart)
        {
            this.workbook = workbook;
            worksheetName = worksheet.worksheetName;
            worksheetName = worksheet.WorksheetName;

            if (worksheet.HasReader())
                worksheetReader = worksheet.GetReader();
            if (worksheet.HasWriter())
                worksheetWriter = worksheet.GetWriter();
        }

        protected virtual void Initialize()
        {
            string cellA = Utils.Util.CellNumberToAddress(startCellIndex);
            using (IWorksheetReader reader = this.GetReader())
            {
                int row = startRowIndex;
                while (reader.Read(cellA + (++row)) != null) { }
                this.endRowIndex = row - 1;

                row = startRowIndex;
                int cell = startCellIndex;
                while (reader.Read(Utils.Util.CellNumberToAddress(++cell) + row) != null) { }
                this.endCellIndex = cell - 1;
            }
        }

        public SharedStringData GetSharedString()
        {
            return workbook.GetSharedString(transaction);
        }

        public string WorksheetName { get { return worksheetName; } }
        Quexcel.OpenXML.Excel.Workbook.Workbook Workbook { get { return workbook; } }
        public XmlNamespaceManager NameSpaceManager
        {
            get { return workbook.NameSpaceManager; }
        }

        public IWorksheetReader GetReader()
        {
            if (worksheetReader == null || worksheetReader.IsDisposed)
                worksheetReader = ReaderWriterBuilder.GetWorksheetReader(SheetPart, workbook.GetSharedString(transaction), NameSpaceManager);
            return worksheetReader;
        }

        public IWorksheetWriter GetWriter()
        {
            if (worksheetWriter == null || worksheetWriter.IsDisposed)
                worksheetWriter = ReaderWriterBuilder.GetWorksheetWriter(SheetPart, workbook.GetSharedString(transaction), NameSpaceManager);
            return worksheetWriter;
        }

        public bool HasWriter()
        {
            return worksheetWriter != null;
        }

        public bool HasReader()
        {
            return worksheetReader != null;
        }

        public Dictionary<string, string> ReadHeader()
        {
            Dictionary<string, string> header = new Dictionary<string, string>();
            Row headerData = this.GetReader().ReadRow(startRowIndex);

            if (headerData == null)
                return null;
            else
            {
                for (int i = 0; i < headerData.Cells.Count; i++ )
                {
                    if (header.ContainsKey(headerData.Cells[i].Value.ToString()))
                        throw new InvalidTableStructureException(WorksheetName, String.Format("Duplicate column [{0}] present in the table [{1}]", headerData[i], WorksheetName));
                    else
                    {
                        header.Add(headerData.Cells[i].Value.ToString().Trim(), Utils.Util.CellNumberToAddress(startCellIndex + i) + startRowIndex);
                    }
                }
            }
            return header;
        }

        public virtual bool AppendRow(string[] dataRow, string[] cellOrder)
        {
            bool isDataInserted = true;

            for (int i = 0; i < dataRow.Length; i++)
            {
                isDataInserted =
                    isDataInserted && this.GetWriter().Write(Utils.Util.AddCellAdress(cellOrder[i].ToString(), 0, this.RowCount + startRowIndex), dataRow[i]);
            }
            if (isDataInserted)
                endRowIndex++;

            return isDataInserted;
        }

        public int RowCount
        {
            get
            {
                if (endRowIndex < 0)
                    return -1;
                else
                    return endRowIndex - startRowIndex;
            }
        }

        public int CellCount
        {
            get
            {
                if (endCellIndex < 0)
                    return -1;
                else
                    return endCellIndex - startCellIndex;
            }
        }

        public IEnumerator<Row> Select()
        {
            int row = startRowIndex;
            IWorksheetReader reader = this.GetReader();
            while (row++ <= endRowIndex)
            {
                yield return reader.ReadRow(row);
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            
        }

        #endregion
    }
}
